﻿/*
 * DATAFLOWCORE
 * 
Copyright 2012 - Mindstorm Multitouch Limited

Author - Bertrand Nouvel

DataFlowCore is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.

DataFlowCore is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU Lesser Public License for more details.
*/



using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using MongoDB.Bson;

namespace DFlow
{



    [AttributeUsage(AttributeTargets.All)]
    public class InputPin : System.Attribute
    {
    }

    [AttributeUsage(AttributeTargets.All)]
    public class OutputPin : System.Attribute
    {
        //public bool DisposeAfterConvert = true;
        //public void DisposeAfterConvert()
        //{
        //}
    }


    [AttributeUsage(AttributeTargets.All)]
    public class MetadataPin : System.Attribute
    {
    }


    [AttributeUsage(AttributeTargets.Class)]
    public class RegisterControlFor : System.Attribute
    {
        static private Dictionary<Pair<System.Type, System.Type>, System.Type> _AllRControlTypes = null;
        static private void HandleNewAssembly(System.Reflection.Assembly a)
        {
            try
            {

                foreach (System.Type t in a.GetExportedTypes())
                {

                    foreach (object o in t.GetCustomAttributes(typeof(RegisterControlFor), false))
                    {
                        _AllRControlTypes.Add(
                            new Pair<System.Type, System.Type>(
                                ((RegisterControlFor)o).AssocitatedUI,
                                ((RegisterControlFor)o).AssocitatedType), t);
                    }
                }
            }
            catch (System.NotSupportedException) { }

        }

        static private void RecomputeAllRControlTypes()
        {
            _AllRControlTypes = new Dictionary<Pair<System.Type, System.Type>, Type>();
            foreach (System.Reflection.Assembly a in AppDomain.CurrentDomain.GetAssemblies())
            {
                HandleNewAssembly(a);
            }
        }
        static public Dictionary<DFlow.Pair<System.Type, System.Type>, System.Type> AllRControlTypes
        {
            get
            {
                if (_AllRControlTypes == null)
                {
                    AppDomain.CurrentDomain.AssemblyLoad += (e, x) => { HandleNewAssembly(x.LoadedAssembly); };
                    RecomputeAllRControlTypes();
                }
                return _AllRControlTypes;
            }
        }



        System.Type _AssociatedUI;
        System.Type _AssociatedType;

        public System.Type AssocitatedType { get { return _AssociatedType; } }
        public System.Type AssocitatedUI { get { return _AssociatedUI; } }
        public RegisterControlFor(System.Type tui, System.Type t)
        {
            _AssociatedUI = tui;
            _AssociatedType = t;
        }
    }

    [AttributeUsage(AttributeTargets.Method)]
    public class SpecificConverter : System.Attribute
    {
    }

    [AttributeUsage(AttributeTargets.All)]
    public class GenericConverter : System.Attribute
    {
        public Func<Type, Type, bool> compatible;
        public GenericConverter(Type t)
        {
            compatible = (t1, t2) => ((bool)t.InvokeMember("Validate",
                System.Reflection.BindingFlags.InvokeMethod, null, null, new object[] { t1, t2 }));
        }
    }


    [AttributeUsage(AttributeTargets.All)]
    public class ExplicitTypeInfoAttribute : System.Attribute
    {
        public string type;
        public ExplicitTypeInfoAttribute(string t) { type = t; }
    }


    [AttributeUsage(AttributeTargets.All)]
    public class ExplicitBsonEncodeFunctionAttribute : System.Attribute
    {
        public Func<Object, BsonValue> encode;
        public ExplicitBsonEncodeFunctionAttribute(Func<Object, BsonValue> t) { encode = t; }
    }

    [AttributeUsage(AttributeTargets.All)]
    public class ThirdPartyReference : System.Attribute
    {
        public string reference;
        public string url;
        public string license;
        public ThirdPartyReference(string r, string u, string l)
        {
            reference = r;
            url = u;
            license = l;
        }
    }


    public abstract class Attribute : System.Attribute
    {
        public abstract void UpdateBson(BsonDocument bd);
        public virtual void AfterUpdateValue(System.Reflection.FieldInfo fi, object o) { }
        public virtual MongoDB.Bson.BsonElement ToBsonElement() { return null; }
    }

    [AttributeUsage(AttributeTargets.All)]
    public class ParamSoftLimits : DFlow.Attribute
    {
        public double min = double.NegativeInfinity;
        public double max = double.PositiveInfinity;
        public ParamSoftLimits() { }
        public ParamSoftLimits(float a, float b) { min = a; max = b; }
        public ParamSoftLimits(double a, double b) { min = a; max = b; }
        public override MongoDB.Bson.BsonElement ToBsonElement() { return new MongoDB.Bson.BsonElement("SoftLimits", new BsonArray(new double[2] { min, max })); }
        public override void UpdateBson(BsonDocument bd)
        {
            bd.Add("SoftLimits", new BsonArray(new double[2] { min, max }));
        }
    }

    [AttributeUsage(AttributeTargets.All)]
    public class ParamHardLimits : ParamSoftLimits
    {
        public ParamHardLimits() { }
        public ParamHardLimits(float a, float b) { min = a; max = b; }
        public ParamHardLimits(double a, double b) { min = a; max = b; }

        public override MongoDB.Bson.BsonElement ToBsonElement() { return new MongoDB.Bson.BsonElement("HardLimits", new BsonArray(new double[2] { min, max })); }

        public override void UpdateBson(BsonDocument bd)
        {
            bd.Add("HardLimits", new BsonArray(new double[2] { min, max }));
        }


        public override void AfterUpdateValue(System.Reflection.FieldInfo fi, object o)
        {
            double f = (double)(fi.GetValue(o));
            if (f < min) { f = min; }
            if (f > max) { f = max; }
            fi.SetValue(o, f);
        }

    }

    [AttributeUsage(AttributeTargets.All)]
    public class EnumValueNames : DFlow.Attribute
    {
        public string[] valuenames = { };
        public EnumValueNames(string[] s) { valuenames = s; }
        public EnumValueNames(Func<string[]> f) { valuenames = f(); }
        public EnumValueNames(System.Type t)
        {
            if (t.IsEnum)
            {
                valuenames = t.GetEnumNames();
            }
            else
            {
                valuenames = ((string[])(t.GetMethod("Eval").Invoke(null, null)));
            }
        }
        public override void UpdateBson(BsonDocument bd)
        {
            bd.Add("EnumValueNames", new BsonArray(valuenames));
        }

    }


    /// [vogon.observer]
    /// <summary>
    /// Observer are nodes that may be used outside of the Dataflow
    /// especially for debugging / visualization purpose.
    /// </summary>
    [AttributeUsage(AttributeTargets.Class)]
    public class Observer : System.Attribute
    {
        static private List<System.Type> _AllObserverTypes = null;
        static private void RecomputeAllObserverTypes()
        {
            _AllObserverTypes = new List<System.Type>();
            foreach (System.Reflection.Assembly a in AppDomain.CurrentDomain.GetAssemblies())
            {
                try
                {
                    foreach (System.Type t in a.GetExportedTypes())
                    {
                        if (t.GetCustomAttributes(typeof(Observer), true).Length != 0)
                        {
                            _AllObserverTypes.Add(t);
                        }
                    }
                }
                catch (System.NotSupportedException)
                {
                    // some (dyn-compiled methods) assemblies to not export types 
                }
            }
        }
        static public List<System.Type> AllObserverTypes
        {
            get
            {
                if (_AllObserverTypes == null)
                {
                    AppDomain.CurrentDomain.AssemblyLoad += (e, x) => { RecomputeAllObserverTypes(); };
                    RecomputeAllObserverTypes();
                }
                return _AllObserverTypes;
            }
        }
    }


}
